package cn.yll.api.util;

import cn.yll.common.exception.BsErrEnum;
import cn.yll.common.exception.BsException;
import cn.yll.common.module.RestModel;
import cn.yll.common.utils.AppContextUtil;
import cn.yll.common.utils.RedisUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.net.URLDecoder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * rest工具类
 * @author yangll
 */
@Slf4j
@Component
public class RestUtil {

    /**
     * psot获取单个对象
     */
    public static <T> RestModel<T> postMode(final String url, final Object params) throws BsException {
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_EMPTY, "the param url is null");
        }
        try {
            log.info("post request start: url= " + url + ", params=" + JSON.toJSONString(params));
            long startDate = System.currentTimeMillis();
            RestModel<T> restModel = getRibbonRestTemplate().postForObject(url, params, RestModel.class);
            log.info("post request complete UseTime: " + (System.currentTimeMillis() - startDate) + " (ms)");
            return restModel;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * psot获取单个对象
     */
    public static <T> T postMode(final String url, final Object params, final Class<T> destClassType) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }
        try {
            RestModel<T> restModel = postMode(url, params);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getData(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * post 方法获取列表
     */
    public static <T> List<T> postModeList(final String url, final Object params, final Class<T> destClassType) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }

        try {
            RestModel<List<T>> restModel = postMode(url, params);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());

            if (restModel.getData() != null && StringUtils.isBlank(restModel.getData().toString())) {
                return Collections.EMPTY_LIST;
            }
            return RestUtil.getModelList(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get 获取map
     *
     * @throws BsException
     */
    public static Map<String, Object> getMapMode(String url) throws BsException {
        try {
            RestModel<Map<String, Object>> restModel = getRestModel(url);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getMap(restModel);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get方法获取单个对象
     */
    public static <T> T getRestModel(String url, final Class<T> destClassType) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }

        try {
            RestModel<T> restModel = getRestModel(url);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getData(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get方式获取返回Response body
     *
     * @param url
     * @return
     */
    public static String getStringForWWW(String url) {
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        try {
            log.info("get request start，url = :" + url);
            long startDate = System.currentTimeMillis();
            url = URLDecoder.decode(url, "UTF-8");
            String result = AppContextUtil.getBean("normalRestTemplate", RestTemplate.class).getForObject(url, String.class);
            log.info("resultModel = " + result + "  completeUseTime: " + (System.currentTimeMillis() - startDate) + " (ms)");
            return result;
        } catch (Exception e) {
            log.error("后台服务请求异常");
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * 获取原生RestModel 不走缓存
     *
     * @throws BsException
     */
    public static RestModel getRestModel(String url) throws BsException {
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        try {
            log.info("get request start，url = :" + url);
            long startDate = System.currentTimeMillis();
            url = URLDecoder.decode(url, "UTF-8");
            RestModel restModel = getRibbonRestTemplate().getForObject(url, RestModel.class);
            log.info("get request complete UseTime: " + (System.currentTimeMillis() - startDate) + " (ms)");
            return restModel;
        } catch (Exception e) {
            log.error("后台服务请求异常");
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get请求获取列表
     */
    public static <T> List<T> getModelList(String url, Class<T> destClassType) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }

        try {
            RestModel<List<T>> restModel = getRestModel(url);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getModelList(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * 获取原生RestModel 并缓存
     *
     * @throws BsException
     */
    public static <T> RestModel<T> getRestModelWithCache(String url, Long cacheTime) throws BsException {
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        if (cacheTime == null || cacheTime == 0) return getRestModel(url);
        RestModel<T> response = null;
        try {
            log.info("get request start，url = :" + url);
            RedisUtil redisUtil = AppContextUtil.getBean(RedisUtil.class);
            long startDate = System.currentTimeMillis();
            String restModel = redisUtil.getV(url);
            if (StringUtils.isBlank(restModel)) {
                url = URLDecoder.decode(url, "UTF-8");
                restModel = getRibbonRestTemplate().getForObject(url, String.class);
                log.info("get request completeUseTime: " + (System.currentTimeMillis() - startDate) + " (ms)");
                if (StringUtils.isNotBlank(restModel)) {
                    response = JSON.parseObject(restModel, RestModel.class);
                    if (RestUtil.isSuccess(response) && response.getData() != null) {
                        redisUtil.set(url, restModel, cacheTime, TimeUnit.SECONDS);
                    }
                }
            } else {
                response = JSON.parseObject(restModel, RestModel.class);
                log.info("缓存的resultModel completeUseTime: " + (System.currentTimeMillis() - startDate) + " (ms)");
            }

            return response;
        } catch (Exception e) {
            log.error("后台服务请求异常");
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get方法获取单个对象 并缓存
     */
    public static <T> T getRestModelWithCache(String url, final Class<T> destClassType, Long cacheTime) throws BsException {
        try {
            RestModel<T> restModel = getRestModelWithCache(url, cacheTime);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getData(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get请求获取列表
     */
    public static <T> List<T> getModelListWithCache(String url, Class<T> destClassType, Long cacheTime) throws BsException {
        try {
            RestModel<List<T>> restModel = getRestModelWithCache(url, cacheTime);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getModelList(restModel, destClassType);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * get 获取map 并缓存
     *
     * @throws BsException
     */
    public static Map<String, Object> getMapModeWithCache(String url, Long cacheTime) throws BsException {
        try {
            RestModel<Map<String, Object>> restModel = getRestModelWithCache(url, cacheTime);
            if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
            if (!RestModel.CODE_SUCCESS.equals(restModel.getCode()))
                throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
            return RestUtil.getMap(restModel);
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * psot获取单个对象 并缓存
     * !!!!!!!!!!!仅适用于post类查询数据接口!!!!!!!!!!!
     */
    public static <T> T postModeWithCache(final String url, final Object params, final Class<T> destClassType, Long cacheTime) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        try {
            RedisUtil redisUtil = AppContextUtil.getBean(RedisUtil.class);
            String key = url + "--" + JSON.toJSONString(params);
            String data = redisUtil.getV(key);
            if (StringUtils.isBlank(data)) {
                RestModel<T> restModel = postMode(url, params);
                if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
                if (!RestUtil.isSuccess(restModel)) throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
                if (restModel.getData() != null) {
                    redisUtil.set(key, JSON.toJSONString(restModel.getData()), cacheTime, TimeUnit.SECONDS);
                }
                return RestUtil.getData(restModel, destClassType);
            } else {
                return JSON.parseObject(data, destClassType);
            }
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * psot获取对象列表 并缓存
     * !!!!!!!!!!!仅适用于post类查询数据接口!!!!!!!!!!!
     */
    public static <T> List<T> postModeForListWithCache(final String url, final Object params, final Class<T> destClassType, Long cacheTime) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        try {
            String key = url + "--" + JSON.toJSONString(params);
            RedisUtil redisUtil = AppContextUtil.getBean(RedisUtil.class);
            String data = redisUtil.getV(key);
            if (StringUtils.isBlank(data)) {
                RestModel<List<T>> restModel = postMode(url, params);
                if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
                if (!RestUtil.isSuccess(restModel)) throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
                List<T> list = null;
                if (restModel.getData() != null) {
                    list = RestUtil.getModelList(restModel, destClassType);
                    if (list != null && list.size() > 0) {
                        redisUtil.set(key, JSON.toJSONString(restModel.getData()), cacheTime, TimeUnit.SECONDS);
                    }
                }
                return list;
            } else {
                return JSON.parseArray(data, destClassType);
            }
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    /**
     * psot获取对象列表 并缓存,查询条件中带有时间，需要传入一个key
     * !!!!!!!!!!!仅适用于post类查询数据接口!!!!!!!!!!!
     */
    public static <T> List<T> postModeForListWithCacheCustomKey(final String url, final Object params, final String keyParams, final Class<T> destClassType, Long cacheTime) throws BsException {
        if (null == destClassType) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param destClassType is null");
        }
        if (StringUtils.isBlank(url)) {
            throw new BsException(BsErrEnum.PARAM_ILLEGAL, "the param url is null");
        }
        try {
            String key = url + "--" + keyParams;
            RedisUtil redisUtil = AppContextUtil.getBean(RedisUtil.class);
            String data = redisUtil.getV(key);
            if (StringUtils.isBlank(data)) {
                RestModel<List<T>> restModel = postMode(url, params);
                if (restModel == null) throw new BsException(BsErrEnum.RPC_ERROR);
                if (!RestUtil.isSuccess(restModel)) throw new BsException(BsErrEnum.RPC_ERROR, restModel.getMessage());
                List<T> list = null;
                if (restModel.getData() != null) {
                    list = RestUtil.getModelList(restModel, destClassType);
                    if (list != null && list.size() > 0) {
                        redisUtil.set(key, JSON.toJSONString(restModel.getData()), cacheTime, TimeUnit.SECONDS);
                    }
                }
                return list;
            } else {
                return JSON.parseArray(data, destClassType);
            }
        } catch (BsException e) {
            log.error("后台服务业务异常");
            throw e;
        } catch (Exception e) {
            log.error("后台服务请求异常", e);
            throw new BsException(BsErrEnum.RPC_ERROR);
        }
    }

    private static RestTemplate getRibbonRestTemplate() {
        RestTemplate restTemplate = AppContextUtil.getBean("restTemplate", RestTemplate.class);
        if (Objects.isNull(restTemplate)) {
            throw new BsException(BsErrEnum.REQUEST_ERROR, "restTemplate组件未配置");
        }
        return restTemplate;
    }

    public static RestModel<?> sucess() {
        return rest(RestModel.CODE_SUCCESS, RestModel.MESSAGE_SUCCESS, null);
    }

    public static RestModel<?> successMsg(String message) {
        return rest(RestModel.CODE_SUCCESS, message, null);
    }

    public static <T> RestModel<T> sucess(T data) {
        return rest(RestModel.CODE_SUCCESS, RestModel.MESSAGE_SUCCESS, data);
    }

    public static <T> RestModel<T> error(String message) {
        return rest(BsErrEnum.SYSTEM_ERROR.getCode(), message, null);
    }

    public static <T> RestModel<T> error(BsException exception) {
        return error(exception.getBsErrEnum());
    }

    public static <T> RestModel<T> error(BsErrEnum errEnum) {
        return rest(errEnum.getCode(), errEnum.getMessage(), null);
    }

    public static <T> RestModel<T> sucess(String message, T data) {
        return rest(RestModel.CODE_SUCCESS, message, data);
    }

    public static <T> RestModel<T> rest(Integer code, String message, T data) {
        RestModel<T> restModel = new RestModel<>(code, message, data);
        log.info("服务返回restModel=" + JSON.toJSONString(restModel));
        return restModel;
    }

    public static <T> T getData(RestModel<T> rest, Class<T> clazz) {
        if ((rest != null) && (rest.getCode() != null) && (rest.getData() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return JSON.parseObject(JSON.toJSONString(rest.getData()), clazz);
        }
        return null;
    }

    public static <T> List<T> getModelList(RestModel<List<T>> rest, Class<T> clazz) {
        if ((rest != null) && (rest.getCode() != null) && (rest.getData() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return JSON.parseArray(JSON.toJSONString(rest.getData()), clazz);
        }
        return null;
    }

    public static <T> Map<String, T> getMap(RestModel<Map<String, T>> rest, Class<T> clazz) {
        if ((rest != null) && (rest.getCode() != null) && (rest.getData() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return JSON.parseObject(JSON.toJSONString(rest.getData()), new TypeReference<Map<String, T>>() {});
        }
        return null;
    }

    public static Map<String, Object> getMap(RestModel<Map<String, Object>> rest) {
        if ((rest != null) && (rest.getCode() != null) && (rest.getData() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return rest.getData();
        }
        return null;
    }

    public static List<Map<String, Object>> getMapList(RestModel<List<Map<String, Object>>> rest) {
        if ((rest != null) && (rest.getCode() != null) && (rest.getData() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return rest.getData();
        }
        return null;
    }

    public static Boolean isSuccess(RestModel<?> rest) {
        if ((rest != null) && (rest.getCode() != null) && (RestModel.CODE_SUCCESS.equals(rest.getCode()))) {
            return true;
        } else {
            return false;
        }
    }
}
